package ru.picman;

import java.util.Random;

import ru.picman.util.EasyMath;
import ru.picman.util.GreenThread;
import android.graphics.Point;

/**
 * This class is the class that handles all the actions of a pig. These
 * action typically include finding the player, and moving to it.
 * 
 * @author Matthijs
 */
public class PigAI
             extends GreenThread
{
	
	/**
	 * Constructs new PigAI.
	 * 
	 * @param pig The pig for this AI
	 */
	public PigAI(Pig pig) {
		this.pig = pig;
	}
	
	/**
	 * One tick for the AI. A new direction for the pig will be chosen
	 * if the current direction is the same as the desired direction.
	 * After the direction check (and change) the pig will be moved.
	 * 
	 * @see GreenThread#run()
	 */
	
	public void run() {
		if (pig.isFrozen())
			return;
		if (pig.getDirection() == pig.getDesiredDirection() && stepCount % (int)(1/pig.getMoveStep()) == 0)
			determineDirection();
			
		pig.move();
		stepCount++;
		
		sleep(moveDelay);
	}
	
	/**
	 * Determines the new (desired) direction for the pig. The direction 
	 * will be chosen at random, but the possible directions are weighted.
	 * The weight of the direction(s) the player is in will be 5, while the
	 * other direction(s) will have a weight of 2. The pigs will only chose
	 * the opposite direction if there is no other possible option. 
	 */
	private void determineDirection() {
		Unit playerUnit = pig.getWorld().getPlayer().getUnit();
		int dx = Integer.signum((int)(playerUnit.getX()-pig.getX()));
		int playerDirX = (dx == 0 ? -1 : dx+2);
		int dy = Integer.signum((int)(playerUnit.getY()-pig.getY()));
		int playerDirY = (dy == 0 ? -1 : dy+1);
		
		int[] weights = new int[4];
		
		int totalWeight = 0;
		for (int i = 0; i < weights.length; ++i)
			if (canMove(i))
				totalWeight += weights[i] = (i == playerDirX || i == playerDirY ? 5 : 2);
		int oppositeDir = (pig.getDirection()+2) % 4;
		if (pig.getDirection() != Unit.DIRECTION_NONE && totalWeight > weights[oppositeDir])
			weights[oppositeDir] = 0;
		
		int direction = calcNextDirection(weights);
		pig.setDesiredDirection(direction);
	}
	
	/**
	 * The integer value of the next direction will be returned, after this
	 * has been calculated. The chosen direction is chosen randomly using
	 * the specified weights. The returned direction is 1, 2, 3 or 4.
	 *  
	 * @param weights The array with weights (must be of length 4)
	 * @return The chosen direction
	 */
	private int calcNextDirection(int[] weights)  {
		if (weights.length != 4)
			throw new IllegalArgumentException("Length of weights array must be 4");
		int totalWeight = 0;
		for (int i = 0; i < weights.length; ++i)
			totalWeight += weights[i];
		int rand = new Random().nextInt(totalWeight);
		for (int i = 0; i < weights.length; ++i) {
			if (rand < weights[i])
				return i;
			rand -= weights[i];
		}
		return Unit.DIRECTION_NONE;
	}
	
	/**
	 * Checks if it is possible to move in the specified direction. This is 
	 * only possible if {@link World#isFree(Point)} returns true.
	 * 
	 * @param direction The direction
	 * @return true if that next tile is free, false otherwise
	 */
	private boolean canMove(int direction) {
		World world = pig.getWorld();
		int width = world.getWidth()-1,
			height = world.getHeight()-1;
		int unitX = EasyMath.clamp((int)pig.getX(),0,width);
		int unitY = EasyMath.clamp((int)pig.getY(),0,height);
		switch (direction) {
			case Unit.DIRECTION_DOWN:
				return world.isFree(new Point(unitX,Math.min((int)unitY+1,height))) && unitY < height;
			case Unit.DIRECTION_LEFT:
				return world.isFree(new Point(Math.max((int)unitX-1,0),unitY)) && unitX-1 > 0;
			case Unit.DIRECTION_RIGHT:
				return world.isFree(new Point(Math.min((int)unitX+1,width),(int)unitY)) && unitX < width;
			case Unit.DIRECTION_UP:
				return world.isFree(new Point((int)unitX,Math.max((int)unitY-1,0))) && unitY-1 > 0;
			default:
				throw new IllegalArgumentException("Illegal direction");
		}
	}
	
	//
	
	private int stepCount;
	
	private final Pig pig;
	private final long moveDelay = 50;
}
